home *** CD-ROM | disk | FTP | other *** search
- /* DoSCSICommand.c */
- /*
- * OriginalSCSI.c
- * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
- * This is a simple sample for the original SCSI manager that shows how to
- * arbitrate for the SCSI bus and send a command using the original SCSI manager.
- * This function is incomplete in that it does not support virtual memory and
- * presumes a six-byte SCSI command block.
- *
- * This sample is further limited in that it always does polled reads.
- *
- * Calling Sequence:
- * OSErr DoSCSICommand(
- * unsigned short targetID,
- * const Ptr scsiCommand,
- * SCSIInstr requestTIB
- * );
- * The parameters have the following meaning:
- *
- * targetID The SCSI Bus ID of the target (0 .. 6). Note that this
- * function can only access LUN zero.
- * scsiCommand The SCSI Command Block (6 bytes).
- * requestTIB The transfer request block.
- *
- * Return codes:
- * noErr normal
- * scCommErr no such device (selection error)
- * scPhaseErr user data buffer was the wrong size for the transfer.
- * You may merely have given a large buffer size to
- * a variable-length request, such as Device Inquiry.
- * sc... other SCSI error.
- * statusErr Device returned "Check condition." The caller should
- * issue a Request Sense SCSI Command to this device.
- * controlErr Device returned "Busy"
- * ioErr Other (serious) device status.
- */
- #include <scsi.h>
- #include <Errors.h>
- #include <OSUtils.h>
- #include <Events.h>
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * SCSI command status (from status phase)
- */
- #define kScsiStatusGood 0x00 /* Normal completion */
- #define kScsiStatusCheckCondition 0x02 /* Need GetExtendedStatus */
- #define kScsiStatusConditionMet 0x04 /* For Compare Command? */
- #define kScsiStatusBusy 0x08 /* Device busy (self-test?) */
- #define kScsiStatusIntermediate 0x10 /* Intermediate status */
- #define kScsiStatusResConflict 0x18 /* Reservation conflict */
- #define kScsiStatusQueueFull 0x28 /* Target can't do command */
- #define kScsiStatusReservedMask 0x3e /* Vendor specific? */
-
- /*
- * This is the maximum number of times we try to grab the SCSI Bus
- */
- #define kMaxSCSIRetries 40 /* 10 seconds, 4 times/sec */
- /*
- * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
- * if either the BSY or SEL bit is set).
- */
- #ifndef kScsiStatBSY
- #define kScsiStatBSY (1 << 6)
- #endif
- #ifndef kScsiStatSEL
- #define kScsiStatSEL (1 << 1)
- #endif
- #define ScsiBusBusy() ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
-
-
- OSErr DoSCSICommand(
- unsigned short targetID,
- const Ptr scsiCommand,
- Ptr requestTIB
- );
-
- OSErr DoSCSICommand(
- unsigned short targetID,
- const Ptr scsiCommand,
- Ptr requestTIB
- )
- {
-
- OSErr status; /* Final status */
- OSErr completionStatus; /* Status from ScsiComplete */
- short totalTries; /* Get/Select retries */
- short getTries; /* Get retries */
- short iCount; /* Bus free counter */
- unsigned long watchdog; /* Timeout after this */
- short stsByte; /* Status byte from device */
- short msgByte; /* Message byte from device */
-
- status = noErr;
- stsByte = msgByte = (-1); /* Preset "bad" values */
- /*
- * Arbitrate for the scsi bus. This will fail if some other device is
- * accessing the bus at this time (which is unlikely).
- *
- *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- *** Do not set breakpoints or call any functions that may require ***
- *** I/O (such as display code that accesses font resources between ***
- *** SCSIGet and SCSIComplete, ***
- *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- */
- for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
- for (getTries = 0; getTries < 4; getTries++) {
- /*
- * Wait for the bus to go free.
- */
- watchdog = TickCount() + 300; /* 5 second timeout */
- while (ScsiBusBusy()) {
- if (TickCount() > watchdog) {
- status = scArbNBErr; /* The bus is busy */
- goto exit; /* Fail */
- }
- }
- /*
- * The bus is free, try to grab it
- */
- for (iCount = 0; iCount < 4; iCount++) {
- if ((status = SCSIGet()) == noErr)
- break;
- }
- if (status == noErr) /* If we get the bus, leave */
- break; /* the "getTries" loop */
- /*
- * The bus became busy again. Try to wait for it to go free.
- */
- for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
- ;
- } /* The getTries loop */
- if (status != noErr) {
- /*
- * If the above loop fails, it means that the SCSI Manager
- * thinks the bus is not busy and not selected, but "someone" has
- * set the internal semaphore that signals that the SCSI Manager
- * itself is busy. The application will have to handle this
- * problem. (We tried getTries * 4 times).
- */
- goto exit;
- }
- /*
- * We now own the SCSI bus. Try to select the device.
- */
- if ((status = SCSISelect(targetID)) != noErr)
- goto exit;
- /*
- * From this point on, we must exit through SCSIComplete() even if an
- * error is detected. Send a command to the selected device. There are
- * several failure modes, including an illegal command (such as a
- * write to a read-only device). If the command failed because of
- * "device busy", we will try it again.
- */
- status = SCSICmd((Ptr) scsiCommand, 6);
- if (status == noErr)
- status = SCSIRead((Ptr) requestTIB);
- finish:
- /*
- * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
- * returning the status and command-completion message bytes..
- */
- completionStatus = SCSIComplete(
- &stsByte,
- &msgByte,
- (5 * 60) /* Five second timeout */
- );
- /*
- * If we have an error here, return as the "final" status.
- *
- */
- if (completionStatus != noErr)
- status = completionStatus;
- else {
- /*
- * ScsiComplete is happy. If the device is busy, Pause for 1/4
- * second and try again.
- */
- if (stsByte == kScsiStatusBusy) {
- Delay(15, (long *) &watchdog);
- continue; /* Do next totalTries attempt */
- }
- }
- /*
- * This is the normal exit (success) or final failure exit.
- */
- break;
- } /* totalTries loop */
- exit:
- /*
- * Return an artificial error if the device returns a non-zero status:
- * statusErr Caller should issue RequestSense.
- * controlErr Device is busy (self-test?) try again later.
- * ioErr Something is dreadfully wrong.
- * scPhaseErr If the device returned good status, the user buffer
- * was larger than was needed. (This will be the case
- * for an inquiry or request sense command.) Since the
- * caller can determine the actual length by examining
- * the TIB or the buffer contents, we can return noErr.
- * Also, there is a bug in the combination of System 7.0.1 and the 53C96
- * that may cause the real SCSI Status Byte to be in the Message byte.
- */
- if (stsByte == kScsiStatusGood
- && msgByte == kScsiStatusCheckCondition)
- stsByte = kScsiStatusCheckCondition;
- if (status == scPhaseErr && stsByte == kScsiStatusGood)
- status = noErr;
- if (status == noErr) {
- switch (stsByte) {
- case kScsiStatusGood: break;
- case kScsiStatusCheckCondition: status = statusErr; break;
- case kScsiStatusBusy: status = controlErr; break;
- default: status = ioErr; break;
- }
- }
- return (status);
- }
-